'''
Created on 24/03/2009

@author: eh01
'''
from httplib import BadStatusLine, CannotSendRequest
from im.ds.dso import DSO
from im.ds.dsstub import DSStub
from im.event import eventconstants
from im.event.event import Event
from im.event.eventmanager import EventManager
from xml.dom import minidom
from socket import error, timeout
import base64
import datetime
import httplib
import threading

class HubListener(threading.Thread):
    '''
    classdocs
    '''
    __stop = False
    __node_id = None
    __conn = None
    __wait = False
    
    def __init__(self, node_id):
        '''
        Constructor
        '''
        ds_stub = DSStub()
        self.__wait = ds_stub.get_hub_wait()
        self.connect_to_hub()
        self.__node_id = node_id
        threading.Thread.__init__(self)

    def connect_to_hub(self):
        """ 
        this creates a http connection based on the HTTP 1.1 Protocol.
        This by default creates a 'keep-alive' connection as we have not 
        specified connection = 'close'
        We are using connection = 'keep-alive' to reduce the response times
        of the hub, by not having to create a new TCP/IP connection each
        time we want to talk to the hub
        """        
        
        try:
            self.__conn = httplib.HTTPConnection(DSStub().get_hub_base_url())
        except Exception, e:
#            print "can't connect"
            print e
            
    def run(self):
        
        # configuration params
        ds = DSStub()
        hub_username = ds.get_hub_username()
        hub_password = ds.get_hub_password()
        hub_base_url = ds.get_hub_base_url()
        
        # encode username and password
        base64string = base64.encodestring(
                '%s:%s' % (hub_username, hub_password))[:-1]
                
        auth_header =  "Basic %s" % base64string # use basic authentication
        
        http_headers = {
             'Authorization':auth_header
             }  
        while not self.__stop: # keep polling hub, until flagged to stop
            try:
                # send request for messages
                print 'polling hub'
                node_response = self.send_get_messages_for_node_id_request(self.__node_id, 
                                                                  http_headers, 
                                                                  hub_base_url)
                print 'polled hub'
                if node_response.status == 200:
                    xml_string = node_response.read() # get body of response
                    xml = minidom.parseString(xml_string) # convert to xml type
                    # look for the first and only messages element
                    messages_element = xml.getElementsByTagName("messages")[0]
                    # look for the id elements
                    ids_element = messages_element.getElementsByTagName("id")
                    for id_element in ids_element: # for each id element
                        print 'Received messages: ' + str(datetime.datetime.now())
                        # assume there is only one child node for the id element
                        child_node = id_element.childNodes[0]
                        # if that element is a text node
                        if child_node.nodeType == child_node.TEXT_NODE:
                            message_id = child_node.data # get content of the text node
                            # send request for message
                            message_response = self.send_get_message_request(self.__node_id, http_headers, hub_base_url, message_id)
                            if message_response.status == 200:
                                xml = message_response.read() # get body of response
                                dso = DSO(xml_string=xml) # convert xml to dso                                       
                                event = Event(eventconstants.HUB_MESSAGE_EVENT_TYPE, dso)
                                EventManager().add_event(event)
                                self.send_delete_message_request(self.__node_id, http_headers, hub_base_url, message_id)
                            else:
                                # what should happen if the message is not there anymore
                                print 'Error: ' + str(message_response.status)
                else:
                    # what should happen if listener cannot connect to hub?
                    print 'Error: ' + str(node_response.status)
            except Exception, e:
                # just print out the error and continue
                print type(e)
                print e
        
    def stop_listener(self):
        self.__stop = True
        self.__conn.close()
        
    def send_get_messages_for_node_id_request(self, node_id, http_headers, hub_base_url):
        request_url = "/node/" + str(node_id)
        if self.__wait:
            request_url += "?wait=true"
        response = self.send_http_request(request_url, http_headers, "GET")
        return response
    
    def send_get_message_request(self, node_id, http_headers, hub_base_url, message_id):
        print 'Request to get message for node id ' + node_id + ' and message id ' + message_id + ' at: ' + str(datetime.datetime.now())
        request_url = "/node/" + str(node_id) + "/" + message_id
        response = self.send_http_request(request_url, http_headers, "GET")        
        print 'Retreived message: ' + str(datetime.datetime.now())
        return response
    
    def send_delete_message_request(self, node_id, http_headers, hub_base_url, message_id):
        request_url = "/node/" + str(node_id) + "/" + message_id
        response = self.send_http_request(request_url, http_headers, "DELETE")
        response.read()

    def create_request_and_send(self, request_url, http_headers, http_method):
        self.__conn.request(method=http_method, url=request_url, headers=http_headers)
        response = self.__conn.getresponse()
        if response.status != 200:
            print response.status
            print response.reason
        else:
            return response

    def send_http_request(self, request_url, http_headers, http_method):
        try:
            return self.create_request_and_send(request_url, http_headers, http_method)
        except BadStatusLine, e:
            # Raised if a server responds with a HTTP status code that we don't understand
            print str(type(e)) + ": " + str(e) + ": reconnect"
            self.connect_to_hub()
            return self.send_http_request(request_url, http_headers, http_method)
        except CannotSendRequest, e:
            # Raised if the hub is not started, so the node cannot make the connection
            print str(type(e)) + ": " + str(e) + ": reconnect"
            self.connect_to_hub()
            return self.send_http_request(request_url, http_headers, http_method)
        except timeout, e:
            print str(type(e)) + ": " + str(e) + ": reconnect"
            self.connect_to_hub()
            return self.send_http_request(request_url, http_headers, http_method)                
        except error, e:
            # This exception is raised for socket-related errors. E.g 
            # - the hub address is incorrect
            print str(type(e)) + ": " + str(e) + ": reconnect"
            self.connect_to_hub()
            return self.send_http_request(request_url, http_headers, http_method)                 
        